cmake_minimum_required (VERSION 3.15)

# Options
option (NRI_STATIC_LIBRARY "Build static library" OFF)

# Options: backends
option (NRI_ENABLE_VK_SUPPORT "Enable VULKAN backend" ON)
option (NRI_ENABLE_D3D11_SUPPORT "Enable D3D11 backend" ON)
option (NRI_ENABLE_D3D12_SUPPORT "Enable D3D12 backend" ON)

# Options: VK specific
option (NRI_ENABLE_XLIB_SUPPORT "Enable 'xlib' support" ON)
option (NRI_ENABLE_WAYLAND_SUPPORT "Enable 'wayland' support" ON)

# Options: D3D12 specific
option (NRI_ENABLE_AGILITY_SDK_SUPPORT "Enable Agility SDK suupport to unlock access to recent D3D12 features" OFF)
set (NRI_AGILITY_SDK_PATH "C:/AgilitySDK" CACHE STRING "Path to a directory containing Agility SDK (contents of '.nupkg/build/native/')")
set (NRI_AGILITY_SDK_VERSION "613" CACHE STRING "Agility SDK version")
set (NRI_AGILITY_SDK_DIR "AgilitySDK" CACHE STRING "Directory where Agility SDK binaries will be copied to relative to 'CMAKE_RUNTIME_OUTPUT_DIRECTORY'")

# Is submodule?
if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
    set (IS_SUBMODULE OFF)
else ()
    set (IS_SUBMODULE ON)
endif ()

# Cached
if (NOT IS_SUBMODULE)
    set (CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "")
    set (CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/_Bin" CACHE STRING "")
endif ()

# Generate PDB for Release builds
if (MSVC)
    set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi")
    set (CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG /OPT:REF /OPT:ICF")
endif ()

# Create project
file (READ "${CMAKE_CURRENT_SOURCE_DIR}/Include/NRI.h" ver_h)
string (REGEX MATCH "NRI_VERSION_MAJOR ([0-9]*)" _ ${ver_h})
set (VERSION_MAJOR ${CMAKE_MATCH_1})
string (REGEX MATCH "NRI_VERSION_MINOR ([0-9]*)" _ ${ver_h})
set (VERSION_MINOR ${CMAKE_MATCH_1})

message ("NRI v${VERSION_MAJOR}.${VERSION_MINOR}")
project (NRI LANGUAGES C CXX)

# Find Windows SDK
if (WIN32)
    if (CMAKE_SIZEOF_VOID_P EQUAL 8)
        set (BIN_ARCHITECTURE "x64")
        set (NVAPI_BIN_ARCHITECTURE "amd64")
    else ()
        set (BIN_ARCHITECTURE "x86")
        set (NVAPI_BIN_ARCHITECTURE "x86")
    endif ()

    if ("$ENV{WIN10_SDK_PATH}$ENV{WIN10_SDK_VERSION}" STREQUAL "")
        get_filename_component (WINDOWS_SDK_PATH "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots;KitsRoot10]" ABSOLUTE CACHE)
        set (WINDOWS_SDK_VERSION ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION})

        if ("${WINDOWS_SDK_VERSION}" STREQUAL "")
            set (WINDOWS_SDK_VERSION ${CMAKE_SYSTEM_VERSION})
            message ("Using CMAKE_SYSTEM_VERSION as WindowsSDK version.(${CMAKE_SYSTEM_VERSION})")
        endif ()
    else ()
        set (WINDOWS_SDK_PATH $ENV{WIN10_SDK_PATH})
        set (WINDOWS_SDK_VERSION $ENV{WIN10_SDK_VERSION})
    endif ()

    if ("${WINDOWS_SDK_PATH}" STREQUAL "")
        message (FATAL_ERROR "Failed to detect WindowsSDK path. Please add enviroment variables WIN10_SDK_PATH and WIN10_SDK_VERSION or use MSVC")
    endif ()

    if ("${WINDOWS_SDK_VERSION}" STREQUAL "")
        message (FATAL_ERROR "Failed to detect WindowsSDK version. Please add enviroment variables WIN10_SDK_PATH and WIN10_SDK_VERSION or use MSVC")
    endif ()

    if (IS_DIRECTORY "${WINDOWS_SDK_PATH}/Include/${WINDOWS_SDK_VERSION}.0")
        set (WINDOWS_SDK_VERSION "${WINDOWS_SDK_VERSION}.0")
    endif ()

    set (WINDOWS_SDK_LIB_PATH "${WINDOWS_SDK_PATH}/Lib/${WINDOWS_SDK_VERSION}/um/${BIN_ARCHITECTURE}")

    find_library (INPUT_LIB_D3D11 NAMES d3d11 HINTS ${WINDOWS_SDK_LIB_PATH} REQUIRED)
    find_library (INPUT_LIB_D3D12 NAMES d3d12 HINTS ${WINDOWS_SDK_LIB_PATH} REQUIRED)
    find_library (INPUT_LIB_DXGI NAMES dxgi HINTS ${WINDOWS_SDK_LIB_PATH} REQUIRED)
    find_library (INPUT_LIB_DXGUID NAMES dxguid HINTS ${WINDOWS_SDK_LIB_PATH} REQUIRED)
endif ()

# Globals?
set_property (GLOBAL PROPERTY USE_FOLDERS ON)

set (CMAKE_CXX_STANDARD 17)
set (CMAKE_CXX_STANDARD_REQUIRED ON)
set (CMAKE_CXX_VISIBILITY_PRESET hidden)
set (CMAKE_CXX_EXTENSIONS OFF)
set (CMAKE_POSITION_INDEPENDENT_CODE ON)
set (CMAKE_POSITION_INDEPENDENT_BINARIES ON)
set (CMAKE_VISIBILITY_INLINES_HIDDEN ON)

if (MSVC AND NOT DEFINED CMAKE_MSVC_RUNTIME_LIBRARY)
    set (CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif ()

# Compile options
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
    set (COMPILE_OPTIONS -msse4.1 -Wextra -Wno-missing-field-initializers)
elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
    set (COMPILE_OPTIONS -msse4.1 -Wextra -Wno-missing-field-initializers)
elseif (MSVC)
    set (COMPILE_OPTIONS /W4 /WX /wd4324)
else ()
    message (WARNING "Unknown compiler!")
endif ()

set (COMPILE_DEFINITIONS WIN32_LEAN_AND_MEAN NOMINMAX _CRT_SECURE_NO_WARNINGS)

# D3D11
if (WIN32 AND NRI_ENABLE_D3D11_SUPPORT)
    message ("NRI adding backend: D3D11")
    set (COMPILE_DEFINITIONS ${COMPILE_DEFINITIONS} NRI_USE_D3D11=1)
    set (INPUT_LIBS_D3D11 ${INPUT_LIB_D3D11} ${INPUT_LIB_DXGI})

    find_library (INPUT_LIB_NVAPI NAMES nvapi64 nvapi PATHS "External/nvapi/${NVAPI_BIN_ARCHITECTURE}" REQUIRED)
    find_library (INPUT_LIB_AGS NAMES amd_ags_${BIN_ARCHITECTURE} PATHS "External/amdags/ags_lib/lib" REQUIRED)

    file (GLOB D3D11_SOURCE "Source/D3D11/*.cpp" "Source/D3D11/*.h" "Source/D3D11/*.hpp")
    source_group ("" FILES ${D3D11_SOURCE})
    file (GLOB D3D11_NVAPI "External/nvapi/*.h")
    source_group ("External/nvapi" FILES ${D3D11_NVAPI})
    file (GLOB D3D11_AMDAGS "External/amdags/ags_lib/inc/*.h")
    source_group ("External/amdags" FILES ${D3D11_AMDAGS})
    add_library (NRI_D3D11 STATIC ${D3D11_SOURCE} ${D3D11_NVAPI} ${D3D11_AMDAGS})
    target_include_directories (NRI_D3D11 PRIVATE "Include" "Source/Shared" "External")
    target_compile_definitions (NRI_D3D11 PRIVATE ${COMPILE_DEFINITIONS})
    target_compile_options (NRI_D3D11 PRIVATE ${COMPILE_OPTIONS})
    target_link_libraries (NRI_D3D11 PRIVATE NRI_Shared ${INPUT_LIBS_D3D11} ${INPUT_LIB_NVAPI} ${INPUT_LIB_DXGUID})
    set_property (TARGET NRI_D3D11 PROPERTY FOLDER ${PROJECT_NAME})
endif ()

# D3D12
if (WIN32 AND NRI_ENABLE_D3D12_SUPPORT)
    message ("NRI adding backend: D3D12")
    set (COMPILE_DEFINITIONS ${COMPILE_DEFINITIONS} NRI_USE_D3D12=1)
    set (INPUT_LIBS_D3D12 ${INPUT_LIB_D3D12} ${INPUT_LIB_DXGI})

    find_library (INPUT_LIB_NVAPI NAMES nvapi64 nvapi PATHS "External/nvapi/${NVAPI_BIN_ARCHITECTURE}" REQUIRED)
    find_library (INPUT_LIB_AGS NAMES amd_ags_${BIN_ARCHITECTURE} PATHS "External/amdags/ags_lib/lib" REQUIRED)

    file (GLOB D3D12_SOURCE "Source/D3D12/*.cpp" "Source/D3D12/*.h" "Source/D3D12/*.hpp")
    source_group ("" FILES ${D3D12_SOURCE})
    file (GLOB D3D12_NVAPI "External/nvapi/*.h")
    source_group ("External/nvapi" FILES ${D3D12_NVAPI})
    file (GLOB D3D12_AMDAGS "External/amdags/ags_lib/inc/*.h")
    source_group ("External/amdags" FILES ${D3D12_AMDAGS})
    add_library (NRI_D3D12 STATIC ${D3D12_SOURCE} ${D3D12_NVAPI} ${D3D12_AMDAGS})
    target_include_directories (NRI_D3D12 PRIVATE "Include" "Source/Shared" "External")
    target_compile_definitions (NRI_D3D12 PRIVATE ${COMPILE_DEFINITIONS})
    target_compile_options (NRI_D3D12 PRIVATE ${COMPILE_OPTIONS})
    target_link_libraries (NRI_D3D12 PRIVATE NRI_Shared ${INPUT_LIBS_D3D12} ${INPUT_LIB_NVAPI})
    set_property (TARGET NRI_D3D12 PROPERTY FOLDER ${PROJECT_NAME})

    if (NRI_ENABLE_AGILITY_SDK_SUPPORT)
        target_include_directories (NRI_D3D12 PRIVATE "${NRI_AGILITY_SDK_PATH}/include")
        target_compile_definitions (NRI_D3D12 PRIVATE NRI_USE_AGILITY_SDK)

        # Copy Agility SDK binaries
        message (STATUS "Copying Agility SDK binaries to '${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${NRI_AGILITY_SDK_DIR}'")
        file(COPY "${NRI_AGILITY_SDK_PATH}/bin/x64" DESTINATION "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${NRI_AGILITY_SDK_DIR}")

        # Generate "NRIAgilitySDK.h"
        file (WRITE Include/NRIAgilitySDK.h
            "// This file is auto-generated during project deployment. Do not modify!\n"
            "#ifdef __cplusplus\n"
            "extern \"C\" {\n"
            "#endif\n\n"
            "__declspec(dllexport) extern const uint32_t D3D12SDKVersion = ${NRI_AGILITY_SDK_VERSION};\n"
            "__declspec(dllexport) extern const char* D3D12SDKPath = \"../${NRI_AGILITY_SDK_DIR}/x64/\";\n\n"
            "#ifdef __cplusplus\n"
            "}\n"
            "#endif\n"
        )
    endif ()
endif ()

# VK
if (NRI_ENABLE_VK_SUPPORT)
    message ("NRI adding backend: VK")
    set (COMPILE_DEFINITIONS ${COMPILE_DEFINITIONS} NRI_USE_VULKAN=1)

    file (GLOB VK_SOURCE "Source/VK/*.cpp" "Source/VK/*.h" "Source/VK/*.hpp")
    source_group ("" FILES ${VK_SOURCE})
    add_library (NRI_VK STATIC ${VK_SOURCE})
    target_include_directories (NRI_VK PRIVATE "Include" "Source/Shared" "External/vulkan/include")
    target_compile_definitions (NRI_VK PRIVATE ${COMPILE_DEFINITIONS})
    target_compile_options (NRI_VK PRIVATE ${COMPILE_OPTIONS})
    target_link_libraries (NRI_VK PRIVATE NRI_Shared)
    set_property (TARGET NRI_VK PROPERTY FOLDER ${PROJECT_NAME})

    if (WIN32)
        target_compile_definitions (NRI_VK PRIVATE VK_USE_PLATFORM_WIN32_KHR)
    elseif (APPLE)
        target_compile_definitions (NRI_VK PRIVATE VK_USE_PLATFORM_METAL_EXT)
    else ()
        if (NRI_ENABLE_XLIB_SUPPORT)
            find_path (XLIB_HEADERS NAMES "X11/Xlib.h")

            if (NOT XLIB_HEADERS)
                message (FATAL_ERROR "Can't find 'X11/xlib.h'.(libx11-dev is not installed?)")
            endif ()

            target_compile_definitions (NRI_VK PRIVATE VK_USE_PLATFORM_XLIB_KHR)
        endif ()

        if (NRI_ENABLE_WAYLAND_SUPPORT)
            find_path (WAYLAND_HEADERS NAMES "wayland-client.h")

            if (NOT WAYLAND_HEADERS)
                message (FATAL_ERROR "Can't find 'wayland-client.h'.(libwayland-dev is not installed?)")
            endif ()

            target_compile_definitions (NRI_VK PRIVATE VK_USE_PLATFORM_WAYLAND_KHR)
        endif ()
    endif ()
endif ()

# Shared
file (GLOB SHARED_SOURCE "Source/Shared/*.cpp" "Source/Shared/*.h" "Source/Shared/*.hpp")
source_group ("" FILES ${SHARED_SOURCE})
add_library (NRI_Shared STATIC ${SHARED_SOURCE})
target_include_directories (NRI_Shared PRIVATE "Include" "Source/Shared")
target_compile_definitions (NRI_Shared PRIVATE ${COMPILE_DEFINITIONS})
target_compile_options (NRI_Shared PRIVATE ${COMPILE_OPTIONS})
set_property (TARGET NRI_Shared PROPERTY FOLDER ${PROJECT_NAME})

# Validation
file (GLOB NRI_VALIDATION_SOURCE "Source/Validation/*.cpp" "Source/Validation/*.h" "Source/Validation/*.hpp")
source_group ("" FILES ${NRI_VALIDATION_SOURCE})
add_library (NRI_Validation STATIC ${NRI_VALIDATION_SOURCE})
target_include_directories (NRI_Validation PRIVATE "Include" "Source/Shared")
target_compile_definitions (NRI_Validation PRIVATE ${COMPILE_DEFINITIONS})
target_compile_options (NRI_Validation PRIVATE ${COMPILE_OPTIONS})
target_link_libraries (NRI_Validation PRIVATE NRI_Shared)
set_property (TARGET NRI_Validation PROPERTY FOLDER ${PROJECT_NAME})

# NRI
file (GLOB NRI_HEADERS "Include/*.h" "Include/*.hpp")
source_group ("Include" FILES ${NRI_HEADERS})

file (GLOB NRI_EXTENSIONS "Include/Extensions/*.h" "Include/Extensions/*.hpp")
source_group ("Include/Extensions" FILES ${NRI_EXTENSIONS})

file (GLOB NRI_SOURCE "Source/Creation/*.cpp" "Source/Creation/*.h")
source_group ("Sources" FILES ${NRI_SOURCE})

file (GLOB NRI_RESOURCES "Resources/*")
source_group ("Resources" FILES ${NRI_RESOURCES})

if (NRI_STATIC_LIBRARY)
    add_library (${PROJECT_NAME} STATIC ${NRI_SOURCE} ${NRI_HEADERS} ${NRI_RESOURCES} ${NRI_EXTENSIONS})
else ()
    add_library (${PROJECT_NAME} SHARED ${NRI_SOURCE} ${NRI_HEADERS} ${NRI_RESOURCES} ${NRI_EXTENSIONS})

    if (WIN32)
        target_compile_definitions (${PROJECT_NAME} PRIVATE "NRI_API=extern \"C\" __declspec(dllexport)")
    else ()
        target_compile_definitions (${PROJECT_NAME} PRIVATE "NRI_API=extern \"C\" __attribute__((visibility(\"default\")))")
    endif ()
endif ()

target_include_directories (${PROJECT_NAME} PRIVATE "Include" "Source/Shared" "External/vulkan/include")
target_compile_definitions (${PROJECT_NAME} PRIVATE ${COMPILE_DEFINITIONS})
target_compile_options (${PROJECT_NAME} PRIVATE ${COMPILE_OPTIONS})

target_link_libraries (${PROJECT_NAME} PRIVATE NRI_Shared NRI_Validation)
if (WIN32)
    target_link_libraries (${PROJECT_NAME} PRIVATE ${INPUT_LIB_DXGI} ${INPUT_LIB_DXGUID}) # for nriReportLiveObjects
else ()
    target_link_libraries (${PROJECT_NAME} PRIVATE ${CMAKE_DL_LIBS})
endif ()

if (WIN32 AND NRI_ENABLE_D3D11_SUPPORT)
    target_link_libraries (${PROJECT_NAME} PRIVATE NRI_D3D11)
endif ()
if (WIN32 AND NRI_ENABLE_D3D12_SUPPORT)
    target_link_libraries (${PROJECT_NAME} PRIVATE NRI_D3D12)
endif ()
if (NRI_ENABLE_VK_SUPPORT)
    target_link_libraries (${PROJECT_NAME} PRIVATE NRI_VK)
endif ()

set_property (TARGET ${PROJECT_NAME} PROPERTY FOLDER ${PROJECT_NAME})

set_target_properties (${PROJECT_NAME} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
message ("NRI output path: '${CMAKE_RUNTIME_OUTPUT_DIRECTORY}'")
